# Select.java

    package org.codefilarete.stalactite.query.model;

    import java.util.Iterator;
    import java.util.Map;

    import org.codefilarete.stalactite.query.model.Selectable.SimpleSelectable;
    import org.codefilarete.tool.collection.KeepOrderMap;
    import org.codefilarete.tool.collection.KeepOrderSet;
    import org.codefilarete.tool.reflect.MethodDispatcher;

    /**
     * A support for the select part of a SQL query
     *
     * @author Guillaume Mary
     */
    public class Select implements FluentSelect<Select> {

    	/** Items in select and their aliases (null value means no alias) */
    	private final KeepOrderMap<Selectable<?>, String> columns;

    	private boolean distinct;

    	public Select() {
    		this.columns = new KeepOrderMap<>();
    		this.distinct = false;
    	}

    	public Select(Select another) {
    		this.columns = new KeepOrderMap<>(another.columns);
    		this.distinct = another.distinct;
    	}

    	@Override
    	public KeepOrderSet<Selectable<?>> getColumns() {
    		return new KeepOrderSet<>(columns.keySet());
    	}

    	@Override
    	public Select add(Iterable<? extends Selectable<?>> expressions) {
    		expressions.forEach(this::add);
    		return this;
    	}

    	@Override
    	public Select add(Selectable<?> expression, Selectable<?>... expressions) {
    		add(expression);
    		for (Selectable<?> expr : expressions) {
    			add(expr);
    		}
    		return this;
    	}

    	@Override
    	public AliasableExpression<Select> add(String expression, Class<?> javaType) {
    		SimpleSelectable<?> selectable = new SimpleSelectable<>(expression, javaType);
    		add(selectable);
    		return new MethodDispatcher()
    				.redirect(Aliasable.class, alias -> {
    					columns.put(selectable, alias);
    					return null;	// we don't care about the returned object since a proxy is returned
    				}, this)
    				.redirect(SelectChain.class, this)
    				.build((Class<AliasableExpression<Select>>) (Class<?>) AliasableExpression.class);
    	}

    	@Override
    	public Select add(Selectable<?> column, String alias) {
    		this.columns.put(column, alias);
    		return this;
    	}

    	@Override
    	public Select add(Map<? extends Selectable<?>, String> aliasedColumns) {
    		this.columns.putAll(aliasedColumns);
    		return this;
    	}

    	public Select remove(Selectable<?> selectable) {
    		this.columns.remove(selectable);
    		return this;
    	}

    	/**
    	 * Gives column aliases. If a {@link Selectable} refers to a null value in returned {@link Map}, it means it has no alias
    	 *
    	 * @return {@link Selectable} aliases of this instance
    	 */
    	@Override
    	public Map<Selectable<?>, String> getAliases() {
    		return new KeepOrderMap<>(columns);
    	}

    	public boolean isDistinct() {
    		return distinct;
    	}

    	@Override
    	public Select distinct() {
    		this.distinct = true;
    		return this;
    	}

    	public void setDistinct(boolean distinct) {
    		this.distinct = distinct;
    	}

    	public KeepOrderMap<Selectable<?>, String> clear() {
    		KeepOrderMap<Selectable<?>, String> result = new KeepOrderMap<>(this.columns);
    		this.columns.clear();
    		return result;
    	}

    	@Override
    	public Iterator<Selectable<?>> iterator() {
    		return this.columns.keySet().iterator();
    	}
    }